package com.ctshk.rpc.payment.strategy.impl;

import cn.hutool.core.util.XmlUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ctshk.common.enums.SystemError;
import com.ctshk.common.utils.HttpUtils;
import com.ctshk.rpc.payment.dto.AliPayConfig;
import com.ctshk.rpc.payment.dto.PayDetailsDTO;
import com.ctshk.rpc.payment.dto.RefundDetailsDTO;
import com.ctshk.rpc.payment.dto.rep.alipay.AliPayCallBackDTO;
import com.ctshk.rpc.payment.dto.rep.alipay.AliPayRefundQueryDTO;
import com.ctshk.rpc.payment.dto.rep.alipay.AliPayTradeQueryDTO;
import com.ctshk.rpc.payment.dto.req.PayRefundReq;
import com.ctshk.rpc.payment.entity.PayRecord;
import com.ctshk.rpc.payment.enums.*;
import com.ctshk.rpc.payment.enums.alipay.AliPayPaymentInst;
import com.ctshk.rpc.payment.factory.PayDetailsDTOSimpleFactory;
import com.ctshk.rpc.payment.factory.RefundDetailsDTOSimpleFactory;
import com.ctshk.rpc.payment.req.PayCommonReq;
import com.ctshk.rpc.payment.strategy.PayStrategy;
import com.ctshk.rpc.payment.utils.PayUtil;
import com.ctshk.rpc.payment.utils.alipay.AliPayCallbackVerify;
import com.ctshk.rpc.payment.utils.alipay.AliPayUtil;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;

/**
 * @BelongsProject: ctshk-cloud
 * @BelongsPackage: com.ctshk.rpc.payment.strategy
 * @Author: hef
 * @CreateTime: 2021-04-20 17:42
 * @Description: 描述
 */
@Component
public class AliPayStrategy extends PayStrategy<AliPayConfig> {
    /**
     * H5的方法
     */
    private static final String H5_SERVICE = "create_forex_trade_wap";
    /**
     * PC方法
     */
    private static final String PC_SERVICE = "create_forex_trade";
    /**
     * App的方法
     */
    private static final String APP_SERVICE = "mobile.securitypay.pay";
    /**
     * 字符集
     */
    private static final String CHARSET = "UTF-8";

    @Value("${spring.profiles.active}")
    private String active;

    public AliPayStrategy() {
        super(AliPayConfig.class, PaymentMethod.ALI_PAY_CH_PC);
    }

    @Override
    public String pay(PayCommonReq req) {
        //获取配置
        AliPayConfig config = getConfig();
        Assert.isTrue(PayCurrency.HKD.equals(req.getCurrency()), "支付宝只支持港币");
        Map<String, String> parameters = new HashMap<>();
        parameters.put("service", getServiceName(req.getPaymentMethod()));
        parameters.put("partner", config.getPartner());
        parameters.put("_input_charset", CHARSET);
        parameters.put("notify_url", config.getNotifyUrl());
        parameters.put("subject", req.getSubject());
        parameters.put("body", req.getBody());
        parameters.put("out_trade_no", String.valueOf(req.getOrderId()));
        parameters.put("currency", req.getCurrency().getCode());
        parameters.put("total_fee", req.getTotalFee().toString());
        parameters.put("refer_url", config.getReferUrl());
        parameters.put("product_code", "NEW_WAP_OVERSEAS_SELLER");
        parameters.put("payment_inst",
                req.getPaymentMethod().getName().contains("HK") ?
                        AliPayPaymentInst.ALIPAYHK.name() :
                        AliPayPaymentInst.ALIPAYCN.name()
        );
        if (req.getPaymentMethod().getName().contains("PC")) {
            parameters.put("qr_pay_mode", "4");
            parameters.put("qrcode_width", "200");
       /* }if (req.getPaymentMethod().getName().contains("H5")) {
            parameters.put("qr_pay_mode", "4");
            parameters.put("qrcode_width", "200");*/
        } else if (req.getPaymentMethod().getName().contains("APP")) {
            //1表示购买
            parameters.put("payment_type", "1");
            parameters.put("seller_id", config.getPartner());
            parameters.put("forex_biz", "FP");
        }
        JSONObject tradeInformation = new JSONObject();
        tradeInformation.put("business_type", "5");
        tradeInformation.put("other_business_type", "旅游产品 ");
        parameters.put("trade_information", tradeInformation.toJSONString());
        String sign = signRsa(config, parameters);
        parameters.put("sign", sign);
        if (req.getPaymentMethod().getName().contains("APP")) {
            return PayUtil.joinParams(parameters);
        } else {
            return PayUtil.generateGetUrl(config.getReqUrl(), CHARSET, parameters);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean payCallback(Map<String, String> paramMap) {
        AliPayConfig config = getConfig();
        if (AliPayCallbackVerify.verify(paramMap, config)) {
            String callbackJson = JSON.toJSONString(paramMap);
            AliPayCallBackDTO dto = JSON.parseObject(callbackJson, AliPayCallBackDTO.class);
            //校验回调类型
            com.ctshk.common.utils.Assert.isTrue(
                    "trade_status_sync".equals(dto.getNotifyType()),
                    SystemError.PAY_22016);
            //获取支付记录
            PayRecord payRecord = payRecordMapper.selectByOrderId(Long.valueOf(dto.getOutTradeNo()));
            if (payRecord == null) return false;
            //校验金额
            /*if ("local".equals(active) || "test".equals(active)) {
                com.ctshk.common.utils.Assert.isTrue(
                        new BigDecimal("0.1").equals(dto.getTotalFee()), SystemError.PAY_22015);
            } else {
                com.ctshk.common.utils.Assert.isTrue(
                        payRecord.getTotalFee().equals(dto.getTotalFee()), SystemError.PAY_22015);
            }*/
            com.ctshk.common.utils.Assert.isTrue(
                    payRecord.getTotalFee().equals(dto.getTotalFee()), SystemError.PAY_22015);

            //修改支付记录
            PayRecord update = new PayRecord();
            update.setId(payRecord.getId());
            //支付状态
            update.setOrderStatus("TRADE_FINISHED".equals(dto.getTradeStatus()) ?
                    PayOrderStatus.FULFIL.getStatus() : PayOrderStatus.PAYMENT_ERROR.getStatus());
            update.setGmtModified(LocalDateTime.now());
            update.setCallbackParam(callbackJson);
            //支付时间
            update.setPayTime(LocalDateTime.now());
            int i = payRecordMapper.updateById(update);
            return i > 0;
        }
        return false;
    }

    @Override
    public PayDetailsDTO getPayDetails(String orderNumber) {
        Map<String, String> map = Maps.newHashMap();
        AliPayConfig config = getConfig();
        map.put("service", "single_trade_query");
        map.put("partner", config.getPartner());
        map.put("_input_charset", "UTF-8");
        map.put("out_trade_no", orderNumber);
        String sign = signRsa(config, map);
        map.put("sign", sign);
        String result = null;
        try {
            result = HttpUtils.sendHttpGet(config.getReqUrl(), map);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.err.println("查看支付宝支付详情返回数据:----" + result);
        Map<String, Object> stringObjectMap = XmlUtil.xmlToMap(result);
        String s = JSON.toJSONString(stringObjectMap);
        AliPayTradeQueryDTO aliPayTradeQueryDTO = JSON.parseObject(s, AliPayTradeQueryDTO.class);
        HashMap response = (HashMap) stringObjectMap.get("response");
        Object trade = response.get("trade");
        aliPayTradeQueryDTO.setResponse(JSON.parseObject(JSON.toJSONString(trade), AliPayTradeQueryDTO.ResponseInfo.class));
        if (aliPayTradeQueryDTO.isSuccess()) {
            //TODO 校验签名
            return PayDetailsDTOSimpleFactory.produce(aliPayTradeQueryDTO.getResponse());
        }
        return null;
    }

    @Override
    public RefundDetailsDTO getRefundDetail(String orderNumber, String refundNumber) {
        AliPayConfig config = getConfig();
        Map<String, String> map = Maps.newHashMap();
        map.put("service", "alipay.acquire.refund.query");
        map.put("partner", config.getPartner());
        map.put("_input_charset", "UTF-8");
        map.put("out_trade_no", orderNumber);
        map.put("out_return_no", refundNumber);
        String sign = signRsa(config, map);
        map.put("sign", sign);
        String result = HttpUtils.sendHttpGet(config.getReqUrl(), map);
        Map<String, Object> xmlMap = XmlUtil.xmlToMap(result);
        AliPayRefundQueryDTO dto = JSON.parseObject(JSON.toJSONString(xmlMap), AliPayRefundQueryDTO.class);
        return RefundDetailsDTOSimpleFactory.produce(dto);
    }

    @Override
    public Object refundNotify(Map<String, String> paramMap) {
        //TODO 退款时is_sync=Y(同步返回) 所以没有回调
        return null;
    }

    @Override
    public boolean refund(PayRefundReq req) {
        AliPayConfig config = getConfig();
        Map<String, String> parameters = new HashMap<>();
        parameters.put("service", "forex_refund");
        parameters.put("partner", config.getPartner());
        parameters.put("_input_charset", "UTF-8");
        parameters.put("out_return_no", req.getRefundNumber());
        parameters.put("out_trade_no", req.getOrderNumber());
        parameters.put("return_amount", req.getRefundAmount().toString());
        parameters.put("currency", req.getCurrency());
        parameters.put("gmt_return", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        parameters.put("reason", req.getRefundReason());
        parameters.put("is_sync", "Y");
        String sign = signRsa(config, parameters);
        parameters.put("sign", sign);
        String result = HttpUtils.sendHttpGet(config.getReqUrl(), parameters);
        Map<String, Object> resultMap = XmlUtil.xmlToMap(result);
        com.ctshk.common.utils.Assert.isTrue(resultMap.get("is_success") != null &&
                resultMap.get("is_success").equals("T"), SystemError.PAY_22010, resultMap.get("error"));
        return true;
    }

    @Override
    protected boolean checkPayDetails(Map<String, String> paramMap) {
        AliPayCallBackDTO dto = JSON.parseObject(JSON.toJSONString(paramMap), AliPayCallBackDTO.class);
        PayDetailsDTO payDetails = getPayDetails(dto.getOutTradeNo());
        BigDecimal total_fee = dto.getTotalFee();
        //状态一致
        com.ctshk.common.utils.Assert.isTrue(
                dto.getTradeStatus().equals(payDetails.getPrimaryStatus()), SystemError.PAY_22017);
        return true;
    }

    /**
     * 获取方法名
     *
     * @param method
     * @return
     */
    private String getServiceName(PaymentMethod method) {
        String name = method.getName();
        if (name.contains("H5")) {
            return H5_SERVICE;
        } else if (name.contains("PC")) {
            return PC_SERVICE;
        } else if (name.contains("APP")) {
            return APP_SERVICE;
        }
        return "";
    }

    /**
     * RSA签名
     *
     * @param config
     * @param parameters
     * @return
     */
    private String signRsa(AliPayConfig config, Map<String, String> parameters) {
        parameters.put("sign_type", "RSA");
        PayUtil.removeUnnecessaryParams(parameters);
        String preSignString = AliPayUtil.generatePreSignString(parameters);
        return AliPayUtil.RSA(preSignString, config.getRsaKey(), CHARSET);
    }

    /**
     * RSA签名
     *
     * @param config
     * @param parameters
     * @return
     */
    private String signRsa2(AliPayConfig config, Map<String, String> parameters) {
        parameters.put("sign_type", "RSA2");
        PayUtil.removeUnnecessaryParams(parameters);
        String preSignString = AliPayUtil.generatePreSignString(parameters);
        return AliPayUtil.RSA2(preSignString, config.getRsaKey(), CHARSET);
    }
}
